#include "UvClient.h"
#include <string.h>
#include "Utils.h"

UvClient::UvClient(uv_tcp_t* handle, int nBufSize)
	: m_pHandle(handle)
	, m_nBufSize(nBufSize)
{
	handle->data = this;
	m_nState = 0;
	m_pRecvBuf = new char[m_nBufSize];

	timespec tp;
	clock_gettime(CLOCK_MONOTONIC_RAW, &tp);

	m_ConnectTime = tp.tv_sec;
}

UvClient::~UvClient()
{
	if (NULL != m_pRecvBuf) {
		delete m_pRecvBuf;
		m_pRecvBuf = NULL;
	}
}

uv_tcp_t* UvClient::GetHandle() const 
{
	return m_pHandle;
}

long UvClient::GetConnectedTime() const
{
	return m_ConnectTime;
}

char* UvClient::GetRecvBuf() const
{
	return m_pRecvBuf;
}

int UvClient::GetBufSize() const
{
	return m_nBufSize;
}

int UvClient::GetState() const
{
	return m_nState;
}

void UvClient::Close()
{
	m_nState = 1;
	uv_close((uv_handle_t*)m_pHandle, OnClose);
}

void UvClient::OnClose(uv_handle_t* handle)
{
	UvClient* pClient = (UvClient*)handle->data;
	pClient->m_nState = 2;
}

static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf)
{
	UvClient* pClient = (UvClient*)handle->data;
	buf->base = pClient->GetRecvBuf();
	buf->len = pClient->GetBufSize();
}

void UvClient::StartRead()
{
	uv_read_start((uv_stream_t*)m_pHandle, alloc_cb, OnRead);
}

void UvClient::OnRead(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
	UvClient* pClient = (UvClient*)stream->data;

	if (nread > 0) {
		pClient->OnRead(buf->base, nread);
	} else if (nread < 0) {
		pClient->Close();
	}
}

bool UvClient::Write(const char* pData, int length, bool bCopy, bool bDelete)
{
	if (NULL==pData || length<=0) {
		return false;
	}

	if (0 != m_nState) {
		return false;
	}

	if (bCopy) {
		char* pDst = new char[length];
		memcpy(pDst, pData, length);

		if (bDelete) {
			delete[] pData;
		}

		pData = pDst;
		bDelete = true;
	}

	uv_buf_t buf = uv_buf_init((char*)pData, length);
	uv_write_t* write = new uv_write_t;
	write->data = (void*)(bDelete ? pData : NULL);

	uv_write(write, (uv_stream_t*)m_pHandle, &buf, 1, OnWritten);

	return true;
}

void UvClient::OnWritten(uv_write_t* write, int status)
{
	delete write;
}
